package cn.com.open.itembank.common;

import cn.com.open.itembank.common.enums.ResourceFileType;
import cn.com.open.itembank.common.enums.SysState;
import cn.com.open.itembank.dto.FilterModelDto;
import cn.com.open.itembank.dto.ResourceResponse;
import cn.com.open.itembank.util.FileUtils;
import cn.com.open.itembank.utils.StringUtil;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.*;
import org.springframework.util.ClassUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * The type Common function.
 */
public class CommonFunction {
    private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(CommonFunction.class);
    /**
     * 表格匹配正则表达式.
     */
    public static final String PATTERN_TABLE_STRING = "<table[^>]*?>[\\s\\S]*?<\\/table>";

    /**
     * 含有附件的正则表达式.
     */
    public static final String PATTERN_ATTACHMENT_STRING = "<[\\s\\S]*? (href|src)=[\\s\\S]*?>";

    /**
     * 标准分隔符.
     */
    public static final String SEPARATOR = "##";

    /**
     * 子试题分隔符.
     */
    public static final String SUB_ITEM_SEPARATOR = "#&#";

    private CommonFunction() {
    }

    //region 将不含有单引号的逗号分隔字符串变化成为含有单引号的逗号分隔字符串

    /**
     * 将不含有单引号的逗号分隔字符串变化成为含有单引号的逗号分隔字符串.
     *
     * @param orginList 原始的不含有单引号的逗号分隔字符串
     * @return the string
     */
    public static String splitList(String orginList) {

        String comma = ",";
        String returnList = orginList;

        if (!StringUtil.isEmpty(returnList)) {

            if (returnList.startsWith(comma)) {
                returnList = returnList.substring(1);
            }
            if (returnList.endsWith(comma)) {
                returnList = returnList.substring(0, orginList.length() - 1);
            }
            returnList = returnList.trim().replace(comma, "','").replace("]", "'").replace("\\[", "'").replaceAll(" ", "");

            return returnList;
        }
        return null;
    }
    //endregion

    //region 得到回收站查询条件用sql

    /**
     * 得到回收站查询条件用sql.
     * <p>
     * activeStatus 活动状态 qq
     * chapterId 章节标识 qq
     * checkStatus 审核状态 qq
     * categoryId 应用类别标识 qq
     *
     * @param filterModelDto the filter model dto
     * @return string string
     */
    public static String recyleBinListSql(FilterModelDto filterModelDto) {

        String activeStatus = filterModelDto.getActiveStatus();
        String chapterId = filterModelDto.getChapterId();
        String checkStatus = filterModelDto.getCheckStatus();
        String categoryId = filterModelDto.getCategoryId();

        StringBuilder sbWhere = new StringBuilder();

        sbWhere.append(CommonFunction.querySql(filterModelDto));

        if (!StringUtil.isEmpty(activeStatus)) {
            sbWhere.append(String.format(" AND q.ActiveStatus = %s", activeStatus));
        }

        if (!StringUtil.isEmpty(chapterId)) {
            sbWhere.append(String.format(" AND q.ChapterId = '%s'", chapterId));
        }

        if (!StringUtil.isEmpty(checkStatus)) {
            sbWhere.append(String.format(" AND q.IsCheck = %s", checkStatus));
        }

        if (!StringUtil.isEmpty(categoryId)) {
            sbWhere.append(String.format(" AND q.QuestionID IN (SELECT c.QuestionID FROM QuestionCategoryRelation c WHERE c.QuestionCategoryID = %s", categoryId));
        }

        return sbWhere.toString();
    }
    //endregion

    //region 得到试题列表查询条件用sql

    /**
     * 得到试题列表查询条件用sql.
     * <p>
     * activeStatus 活动状态
     * chapterList 章节标识列表
     * checkStatus 审核状态
     * categoryList 应用类别列表
     * excludeIdList 排除试题列表
     * includeIdList 包含试题列表
     * isOuter  是否外部
     *
     * @param filterModelDto the filter model dto
     * @param isOuter        the is outer
     * @return the string
     */
    public static String questionListSql(FilterModelDto filterModelDto, boolean isOuter) {

        String chapterList = filterModelDto.getChapterList();
        String includeIdList = filterModelDto.getIncludeIdList();
        String excludeIdList = filterModelDto.getExcludeIdList();
        String categoryList = filterModelDto.getCategoryList();
        String activeStatus = filterModelDto.getActiveStatus();
        String checkStatus = filterModelDto.getCheckStatus();

        StringBuilder strBuilder = new StringBuilder();

        strBuilder.append(querySql(filterModelDto));

        if (!StringUtil.isEmpty(includeIdList)) {
            strBuilder.append(String.format(" AND q.[QuestionID] IN ('%s')", splitList(includeIdList)));
        }

        if (!StringUtil.isEmpty(excludeIdList)) {
            strBuilder.append(String.format(" AND q.[QuestionID] NOT IN ('%s')", splitList(excludeIdList)));
        }

        if (!StringUtil.isEmpty(categoryList)) {
            String categorySql = String.format(" SELECT DISTINCT QuestionID FROM QuestionCategoryRelation WHERE QuestionCategoryID IN ('%s')", splitList(categoryList));
            strBuilder.append(String.format(" AND q.QuestionID IN (%s)", categorySql));
        }

        //如果给定查询条件，那么activeStatus=0查询启用，为1查询停用，否则只能查询停用和启用；删除和系统停用不能被查询。
        if (!StringUtil.isEmpty(activeStatus)) {
            strBuilder.append(String.format(" AND qur.[Status]=%d", (Integer.parseInt(activeStatus) == 0 ? SysState.START.getIndex() : SysState.STOP.getIndex())));
        } else {
            strBuilder.append(String.format(" AND (qur.[Status]=%d OR qur.[Status]=%d)", SysState.CLOSE.getIndex(), SysState.START.getIndex()));
        }

        if (!StringUtil.isEmpty(chapterList)) {
            strBuilder.append(String.format(" AND qc.[ChapterID] IN (%s)", splitList(chapterList)));
        }

        if (isOuter) {
            strBuilder.append(String.format(" AND q.[IsCheck]=%d", SysState.START.getIndex()));
        } else if (!StringUtil.isEmpty(checkStatus)) {
            strBuilder.append(String.format(" AND q.[IsCheck]=%d", Integer.parseInt(checkStatus)));
        }
        return strBuilder.toString();
    }
    //endregion

    //region 得到试题通用查询sql

    /**
     * 得到试题通用查询sql.
     * itembankId 课程标识
     * questionTypeId 题型标识
     * code 试题代码
     * perceiverId 认知分类
     * masterLevel 掌握程度
     * minDifficult 最小难度
     * maxDifficult 最大难度
     * beginTime 开始时间
     * endTime 结束时间
     *
     * @param filterModelDto the filter model dto
     * @return the string
     */
    public static String querySql(FilterModelDto filterModelDto) {

        String itembankId = filterModelDto.getItembankId();
        String questionTypeId = filterModelDto.getQuestionTypeId();
        String code = filterModelDto.getCode();
        String minDifficult = filterModelDto.getMinDifficult();
        String maxDifficult = filterModelDto.getMaxDifficult();
        String perceiverId = filterModelDto.getPerceiverId();
        String masterLevel = filterModelDto.getMasterLevel();
        String beginTime = filterModelDto.getBeginTime();
        String endTime = filterModelDto.getEndTime();

        StringBuilder strBuilder = new StringBuilder();

        strBuilder.append(String.format(" AND qur.ItemBankID='%s'", itembankId));

        if (!StringUtil.isEmpty(questionTypeId)) {
            strBuilder.append(String.format(" AND q.[QuestionTypeID]='%s'", questionTypeId));
        }
        if (!StringUtil.isEmpty(code)) {
            strBuilder.append(String.format(" AND (q.[SerialNumber] LIKE '%%%s%%' OR q.[Title] LIKE '%%%s%%')", code, code));
        }
        if (!StringUtil.isEmpty(perceiverId)) {
            strBuilder.append(String.format(" AND q.[Perceiver]=%d", Integer.parseInt(perceiverId)));
        }
        if (!StringUtil.isEmpty(masterLevel)) {
            strBuilder.append(String.format(" AND q.[MasterLevel]=%d", Integer.parseInt(masterLevel)));
        }
        if (!StringUtil.isEmpty(beginTime)) {
            strBuilder.append(String.format(" AND q.[CreateTime]>=%s", beginTime));
        }
        if (!StringUtil.isEmpty(endTime)) {
            strBuilder.append(String.format(" AND q.[CreateTime]<=%s", endTime));
        }
        if (!StringUtil.isEmpty(minDifficult)) {
            strBuilder.append(String.format(" AND q.Difficulty >= %f", new BigDecimal(minDifficult)));
        }
        if (!StringUtil.isEmpty(maxDifficult)) {
            strBuilder.append(String.format(" AND q.Difficulty <= %f", new BigDecimal(maxDifficult)));
        }

        return strBuilder.toString();
    }
    //endregion

    //region 根据试题模型的i6选项列表得到word格式的选项文本

    /**
     * 根据试题模型的i6选项列表得到word格式的选项文本
     *
     * @param optionList the option list
     * @return choice data
     */
    public static String getChoiceData(List<String> optionList) {
        StringBuilder choiceStr = new StringBuilder();

        if (optionList == null || optionList.isEmpty()) {
            return choiceStr.toString();
        }

        for (int i = 0; i < optionList.size(); i++) {
            choiceStr.append(String.format("%s.%s\r", Function.byteAsciiToChar(65 + i), optionList.get(i)));
        }
        choiceStr.setLength(choiceStr.length() - 1);

        return choiceStr.toString();
    }
    //endregion

    //region 根据试题模型的i7答案列表得到word格式的答案文本

    /**
     * 根据试题模型的i7答案列表得到word格式的答案文本
     *
     * @param answerList the answer list
     * @return answer data
     */
    public static String getAnswerData(List<String> answerList) {
        StringBuilder answerStr = new StringBuilder();

        if (answerList == null || answerList.isEmpty()) {
            return answerStr.toString();
        }

        for (int i = 0; i < answerList.size(); i++) {
            answerStr.append(String.format("%s##", Function.byteAsciiToChar(65 + Integer.valueOf(answerList.get(i)))));
        }

        answerStr.setLength(answerStr.length() - 2);

        return answerStr.toString();
    }
    //endregion

    //region 根据试题模型的i6答案列表得到word格式的答案文本

    /**
     * 根据试题模型的i6答案列表得到word格式的答案文本
     *
     * @param answerList the answer list
     * @return text entry answer data
     */
    public static String getTextEntryAnswerData(List<String> answerList) {
        StringBuilder answerStr = new StringBuilder();

        if (answerList == null || answerList.isEmpty()) {
            return answerStr.toString();
        }

        for (int i = 0; i < answerList.size(); i++) {
            answerStr.append(String.format("%s##", answerList.get(i)));
        }

        answerStr.setLength(answerStr.length() - 2);

        return answerStr.toString();
    }
    //endregion

    //region 将难度数字翻译成汉字表述

    /**
     * 将难度数字翻译成汉字表述
     *
     * @param item the item
     * @return the difficulty
     */
    public static String getDifficulty(double item) {
        String str = "";
        if (item >= 0 && item <= 0.1) {
            str = "难";
        } else if (item > 0.1 && item <= 0.3) {
            str = "较难";
        } else if (item > 0.3 && item <= 0.6) {
            str = "中";
        } else if (item > 0.6 && item <= 0.8) {
            str = "较易";
        } else {
            str = "易";
        }
        return str;
    }
    //endregion

    //region 格式化成html

    /**
     * 格式化成html.
     *
     * @param orginString the orgin string
     * @return the string
     */
    public static String formatToHtml(String orginString) {
        return orginString.replace("&lt;", "<").replace("&gt;", ">");
    }
    //endregion

    //region 得到文件绝对路径，并且清理给定路径下一天前的文件

    /**
     * 得到文件绝对路径，并且清理给定路径下一天前的文件.
     *
     * @param pathName the path name
     * @param fileName the file name
     * @return the file
     */
    public static String getFileFullPath(String pathName, String fileName) {
        //导出文件夹路径
        String filePath = String.format("%s%s", ClassUtils.getDefaultClassLoader().getResource("").getPath(), pathName);
        //创建文件夹
        FileUtils.createDir(filePath);
        //删除给定文件夹下的最后编辑时间和当前时间差大于1天的历史文件
        FileUtils.deleteOldFiles(new File(filePath));

        return String.format("%s%s%s", filePath, File.separator, fileName);
    }
    //endregion

    //region 从题干或者选项原始内容中获取含有图片的地址列表

    /**
     * 从题干或者选项原始内容中获取含有图片的地址列表，如果是http的则直接返回，如果是本地磁盘的则需要格外关注.
     *
     * @param orginString the orgin string
     * @return the list
     */
    public static List<String> findAttachmentPathList(String orginString) {
        String patternString = "(?<=(href|src)=)(?<Q>\"|')(?<PATH>.*?)\\k<Q>";   //(?<=(href|src)=)(?<Q>"|')(?<PATH>.*?)\k<Q>
        Pattern p = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);

        Matcher matcher = p.matcher(orginString);
        List<String> returnList = new ArrayList<>();
        while (matcher.find()) {
            String fullPath = matcher.group("PATH").replace("", "");

            returnList.add(fullPath);
        }
        return returnList;
    }
    //endregion

    //region 得到题干或者选项原始内容中所有表格里面单元格的内容列表

    /**
     * 得到题干或者选项原始内容中所有表格里面单元格的内容列表
     *
     * @param orginTableContent the orgin table content
     * @return the table content
     */
    public static List<List<List<String>>> getTableContent(String orginTableContent) {
        List<List<List<String>>> listTable = new ArrayList<>();

        if (orginTableContent.toLowerCase().indexOf("<table") > -1) {
            orginTableContent = orginTableContent.replaceAll("(?i)<table>", "")
                    .replaceAll("(?i)<thead>", "")
                    .replaceAll("(?i)</thead>", "")
                    .replaceAll("(?i)<tbody>", "")
                    .replaceAll("(?i)</tbody>", "")
                    .replaceAll("(?i)<tfoot>", "")
                    .replaceAll("(?i)</tfoot>", "")
                    .replaceAll("(?i)<th>", "<td>")
                    .replaceAll("(?i)</th>", "</td>");
            String[] arrayTable = orginTableContent.split("(?i)</table>");

            for (int i = 0; i < arrayTable.length; i++) {
                String trStr = arrayTable[i].replaceAll("(?i)<tr>", "");
                String[] arrayTrs = trStr.split("(?i)</tr>");
                List<List<String>> listTr = new ArrayList<>();

                for (int j = 0; j < arrayTrs.length; j++) {
                    String tdStr = arrayTrs[j].replaceAll("(?i)<td>", "");
                    String[] arrayTds = tdStr.split("(?i)</td>");
                    List<String> listTd = new ArrayList<>();

                    for (int k = 0; k < arrayTds.length; k++) {
                        listTd.add(arrayTds[k]);
                    }
                    listTr.add(listTd);
                }
                listTable.add(listTr);
            }
        }

        return listTable;
    }
    //endregion

    //region 得到题干或者选项原始内容中所有不含指定匹配内容的内容列表

    /**
     * 得到题干或者选项原始内容中所有不含指定匹配内容的内容列表.
     *
     * @param orginTableContent 原始字符串
     * @param patternString     匹配字符串
     * @return the string [ ]
     */
    public static String[] getNonPatternContent(String orginTableContent, String patternString) {
        Pattern p = Pattern.compile(patternString);
        return p.split(orginTableContent);
    }
    //endregion

    //region 得到根据分隔符拆分的内容集合

    /**
     * 得到根据分隔符拆分的内容集合.
     *
     * @param originalString 含有分隔符的内容原始文本
     * @param separator      分隔符
     * @return the list
     */
    public static List<String> getList(String originalString, String separator) {
        return Arrays.asList(originalString.split(separator));
    }

    /**
     * Gets list remove empty entities.
     *
     * @param originalString the original string
     * @param separator      the SEPARATOR
     * @return the list remove empty entities
     */
    public static List<String> getListRemoveEmptyEntities(String originalString, String separator) {
        List<String> list = getList(originalString, separator);
        List<String> result = new ArrayList<>();
        for (String str : list) {
            if (str.trim().length() != 0) {
                result.add(str);
            }
        }

        return result;
    }
    //endregion

    //region 用标准分隔符拼接字符串集合

    /**
     * 用标准分隔符拼接字符串集合.
     *
     * @param stringList the string list
     * @return the each list
     */
    public static String getEachList(List<String> stringList) {
        return String.join(SEPARATOR, stringList);
    }
    //endregion

    //region 文件获取

    /**
     * region根据资源id获取资源路径(resources).
     *
     * @param resourceApiUrl the resource api url
     * @param appkey         the appkey
     * @param appSecret      the app secret
     * @param resourceId     the resource id
     * @param fileType       the file type
     * @return the upload file path by resources
     * @throws IOException the io exception
     */
    public static String getPathByResources(String resourceApiUrl, String appkey, String appSecret, String resourceId, ResourceFileType fileType) throws IOException {
        Map<String, String> headerMap = new HashMap<>();
        headerMap.put("appkey", appkey);
        headerMap.put("appsecret", appSecret);

        //路径参数
        StringBuffer buffer = new StringBuffer("transcodeLevel=-1");
        //文件类型
        buffer.append(String.format("&type=%d", fileType.getIndex()));
        //获取地址
        String httpurl = httpGet(resourceApiUrl + "resources/" + resourceId + "/requestUrl?" + buffer, headerMap);

        ResourceResponse result = ResourceResponse.init(httpurl);

        if (!result.isSuccess()) {
            return "";
        }

        Map<String, String> payload = (Map<String, String>) result.getPayload();
        //得到地址路径
        return payload.get("url");
    }

    /**
     * Gets upload file path by resources.
     *
     * @param requestUrl the request url
     * @param headerMap  the header map
     * @return upload file path by resources
     */
    private static String httpGet(String requestUrl, Map<String, String> headerMap) throws IOException {
        StringBuffer result = new StringBuffer();

        try {
            URL url = new URL(requestUrl);

            URLConnection conn = url.openConnection();
            //设置超时间为3秒
            conn.setConnectTimeout(3 * 1000);
            //防止屏蔽程序抓取而返回403错误
            conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
            conn.setRequestProperty("Charset", "utf-8");

            for (Map.Entry<String, String> header : headerMap.entrySet()) {
                conn.setRequestProperty(header.getKey(), header.getValue());
            }
            logger.info(String.format("httpGet | url is :%s", requestUrl));
            InputStream input = conn.getInputStream();

            int ss;
            while ((ss = input.read()) != -1) {
                result.append((char) ss);
            }
            logger.info(String.format("httpGet | url is :%s,result is :%s", requestUrl, result.toString()));
        } catch (Exception e) {
            e.printStackTrace();
        }

        return new String(result.toString().getBytes("iso8859-1"), "utf-8");
    }
    //endregion

    //region 文件上传

    /**
     * Upload file to resource service string.
     *
     * @param resourceApiUrl the resource api url
     * @param appkey         the appkey
     * @param appSecret      the app secret
     * @param file           the file
     * @param fileType       the file type
     * @return the string
     * @throws IOException the io exception
     */
    public static String uploadFileToResourceService(String resourceApiUrl, String appkey, String appSecret, File file, ResourceFileType fileType) throws IOException {
        Map<String, String> headerMap = new HashMap<>();
        headerMap.put("appkey", appkey);
        headerMap.put("appsecret", appSecret);

        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("type", fileType.getIndex());
        return uploadFile(resourceApiUrl + "resources/upload", file, "file", headerMap, paramMap);
    }

    /**
     * @param url       上传路径
     * @param file      文件路径
     * @param fileKey   fileKey
     * @param headerMap 上传路径头参数
     * @param paramMap
     * @return
     */
    public static String uploadFile(String url, File file, String fileKey, Map<String, String> headerMap, Map<String, Object> paramMap) {
        final RestTemplate restTemplate = new RestTemplate();
        FileSystemResource fileResource = new FileSystemResource(file);
        HttpHeaders headers = new HttpHeaders();
        if (headerMap != null && headerMap.size() > 0) {
            headerMap.forEach(headers::add);
        }
        MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
        param.add(fileKey, fileResource);
        if (paramMap != null && paramMap.size() > 0) {
            paramMap.forEach(param::add);
        }
        HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(param, headers);
        logger.info(String.format("http upload file | url is :%s", url));
        ResponseEntity<String> result = restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class);
        logger.info(String.format("http upload file | url is :%s,result is :%s", url, result.toString()));
        if (result.getStatusCode() == HttpStatus.OK) {
            return result.getBody();
        }
        return "";
    }

    //endregion

    //endregion
    /**
     * 剔除HTML标签
     * @param content
     */
    public static String processeHTML(String content) {
        Pattern pHtml = Pattern.compile("<[.[^>]]*>", Pattern.CASE_INSENSITIVE);
        Matcher mHtml = pHtml.matcher(content);  // 过滤html标签
        return mHtml.replaceAll("");
    }
}
